/*  Nanhill Simple Remote object(sro) Protocol JavaScript Library
 * 
 *  version 1.0.1 2006-03-31
 * 		remove server response sro protocol in 1.0, use pure javascript instead,
 * 		and modify the relevant code especially in Sro.Reply 
 * 
 *  (c) 2005 alin <xalinx@gmail.com>
 *	need nhill util.js, prototype.js
 * 
 *  Nanhill javascript library is freely distributable under the terms 
 * 	of an Apache license. For details, see the Nanhill web site: 
 *  http://sourceforge.net/projects/nanhillplatform
/*--------------------------------------------------------------------------*/
nh.Sro = Class.create();
nh.Sro._METHOD_ID = "SRO_METHOD_ID";
nh.Sro._SERVICE_ID = "SRO_SERVICE_ID";
nh.Sro.prototype = {
	initialize: function(gateway, async, events) {
		this.gateway = gateway;
		this._transport = null;
		this._async = async ? async : true;
		this._callback = new Function();
		this._setEvents(events);
	},
	
	getGateway : function() { return this.gateway;},

	remoteCall: function(serviceName, params, callback) {
		var loc = serviceName.split(".");	
		//Server controller is MultiControllerExporter, need service and method id.
		if(2 <= loc.length ) {
			var url = this.gateway + "?" + nh.Sro._SERVICE_ID + "=" + loc[0] + "&" + nh.Sro._METHOD_ID + "=" + loc[1];			
		//Server controller is SimpleControllerExporter, need method id.
		}else if (1 == loc.length) {
			var url = this.gateway+ "?" + nh.Sro._METHOD_ID + "=" + loc[0];
		}	
		var call = new nh.Sro.Call();
		for (var i = 0; i < params.length; i++) {
			call.addParameter(params[i]);
		}
		//alert(call.xml());
		this._transport = nh.Util.createXmlHttp();
		this._transport.open("POST", url, this._async);
		this._transport.send(call.xml());
		this._callback = callback;
		if (this._async) {
			this._transport.onreadystatechange = this._onStateChange.bind(this);
			this._events["onLoading"](true);
		} else { 
			this._response();
		}
	},

	_setEvents: function(events) {
		this._events = {
			onLoading: nh.Sro._Default._showLoading,
			onFinish: new Function(),
			onException: new Function(),
			onError: nh.Sro._Default._showError
		};
		Object.extend(this._events, events || {});
	},
	
	_onStateChange: function(){
		if (this._transport.readyState == 4) {
			this._response();
		}
	},
	
	_response : function() {
		if (this._transport.status == '200') {
			var data = this._transport.responseText;
			this._events["onLoading"](false);
			//alert(data);
			var reply = new nh.Sro.Reply(data);
			this._callback(reply);
			this._events["onFinish"](reply);
		} else {
			this._events["onError"](this._transport.responseText);
		}
	}

}


nh.Sro._Default = {
	_loadingPane:null,
	_errorPane:null,
	
	_showLoading : function(state) {
		this._loadingPane = $("sro_loading");
		if (this._loadingPane == null) {
			var el = document.createElement('DIV');
			el.id = "sro_loading";
			el.style.cssText="display:none;top:1px;font-family:Verdana;font-size:11px;border:1px solid #00CC00;background-color:#A4FFA4;padding:1px;position:absolute; right:1px; width:110px; height:14px; z-index:10000";
			el.innerHTML="data loading... ";
			document.body.appendChild(el);
			this._loadingPane = el;
		}
		if (state) {
			this._loadingPane.style.display="block";
			//this.loadingPane.style.top = document.body.scrollTop+1;
		} else {
			this._loadingPane.style.display="none";
		}
	},
	
	_showError: function(errorStr) {
		this._errorPane = $("sro_error");
		if (this._errorPane == null) {
			var el = document.createElement('DIV');
			el.setAttribute("id","sro_error");
			el.style.cssText="font-family:Verdana;font-size:11px;border:1px solid #00CC00;background-color:#ffdb9c;padding:1px;position:absolute;overflow:auto; right:1px; width:300px; height:200px; z-index:1";
			el.innerHTML=errorStr;
			document.body.appendChild(el);
			this._errorPane = el;
		}
	},
	
	showException: function(ex) { /*TODO*/ }
}



nh.Sro.Call = Class.create();
nh.Sro.Call.prototype = {
	initialize: function(){
		this.params = [];
		this.intMin = -2147483648;
		this.intMax = 2147483647;
	},

	addParameter: function(data){
		if (arguments.length==0) return;
		this.params[this.params.length] = data;
	},

	xml: function(){
		var xmlstr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
		xmlstr += "<sro ver=\"1.0\">\n";
		xmlstr += this._getParamXML(this._dataTypeOf(this.params),this.params);
		xmlstr += "</sro>";
		return xmlstr;   
	},
	
	/* get sro type of an javascript object */
	_dataTypeOf: function (obj){
		var type;
		if (null==obj)
			type="null";
		else{
			type=(typeof(obj)).toLowerCase();
			switch(type){
				case "string":
					break;
				case "boolean":
					break;
				case "number":
					if (Math.round(obj) == obj && this.intMin < obj && this.intMax > obj ) { 
						type = "int"; 
					} else {
						type = "double";
					}
					break;
				case "object":
					var con = obj.constructor;
					if (con == Date) {
						type = "date";
					} else if (con == Array) {
						type = "list";
					} else if (con == Object){
						type = "map";
					} else {
						alert(nh.Util.typeOf(obj) + " is not a sro type!");
					}
					break;
				case "function":
					alert("Function is not a sro type!");
					break;
			}
		}
		return type;
	},
	
	_doIntegerXML: function(data) {
		var xml = "<int>" + data + "</int>";
		return xml;
	},
	
	_doDoubleXML: function(data) {
		var xml = "<double>" + data + "</double>";
		return xml;
	},

	_doBooleanXML: function(data){
		var value = (data==true)?1:0;
		var xml = "<boolean>" + value + "</boolean>";
		return xml;
	},
	
	_doStringXML: function(data){
		var xml = data;
		xml = xml.replace(/&/g,"&amp;");
		xml = xml.replace(/</g,"&lt;");
		xml = xml.replace(/>/g,"&gt;");
		xml = "<string>" + xml + "</string>";
		return xml;
	},
	
	_doDateXML: function(data){
		var xml = "<date>";
		xml += data.getTime();
		xml += "</date>";
		return xml;
	},
	
	_doArrayXML : function(data){
		var xml = "<list>\n";
		xml += "<size>";
		var size = data.length;
		xml += size;
		xml += "</size>\n";
		for (var i = 0; i < size; i++){
			xml += this._getParamXML(this._dataTypeOf(data[i]),data[i]);
		}
		xml += "</list>";
		return xml;
	},
	
	_doMapXML : function(data){
		var xml = "<map>\n";
		for (var i in data){
			if (typeof(data[i]) == "function") {
				continue; /* the function shouldn't transfered. */
			}
			xml += this._getParamXML(this._dataTypeOf(i),i);
			xml += this._getParamXML(this._dataTypeOf(data[i]),data[i]);
		}
		xml += "</map>";
		return xml;
	},
	
	_doNullXml : function() {
		return "<null></null>";
	},
	
	_getParamXML: function(type,data){
		var xml;
		switch (type){
			case "string": xml = this._doStringXML(data); break;
			case "list": xml = this._doArrayXML(data); break;
			case "map": xml = this._doMapXML(data); break;
			case "int": xml = this._doIntegerXML(data); break;
			case "date": xml = this._doDateXML(data); break;
			case "double": xml = this._doDoubleXML(data); break;
			case "boolean": xml = this._doBooleanXML(data); break;
			case "null": xml = this._doNullXml(); break;
			default: 
				alert("type:[" + type + "] " + "has no serializer!");
		}
		xml +=  '\n';
		return xml;
	}
}

nh.Sro.Reply = Class.create();
nh.Sro.Reply.prototype = {
	initialize: function(sourceXML) {
		this._source = sourceXML;
		var sro_result;
		eval("sro_result = " + this._source);
		this._result = sro_result;
		this._isFault = false;
		this._type = "null";
	},
	
	getType: function() { return this._type; },
	
	getResult : function() { return this._result; },
	
	isFault : function() { return (this._type == "fault"); },
	
	isNull: function() { return (this._type == "null"); },
	
	getSource : function() { return this._source; }
}

